package com.cellngine;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;

public class Logger
{
	private String			path;
	private File			log_file;
	private BufferedWriter	file_io;
	
	public Logger()
	{
		this(null, false);
	}
	
	public void dispose()
	{
		if (this.file_io != null)
		{
			try
			{
				this.file_io.close();
			}
			catch (final IOException e)
			{
				this.echo(e, Constants.log_type.error);
			}
		}
	}
	
	public Logger(final String path)
	{
		this(path, false);
	}
	
	public Logger(final String path, final boolean append_date)
	{
		if (path != null)
		{
			this.path = cellngine.getPath() + path;
			if (append_date)
			{
				this.path += Constants.date_format_logger_file.format(new Date());
			}
			this.path += ".txt";
			
			this.echo(this.path);
			
			this.log_file = new File(this.path);
			
			// Automatically creates the log directory if it doesn't exist.
			if (this.log_file.getParentFile() != null && !this.log_file.getParentFile().exists())
			{
				this.log_file.getParentFile().mkdirs();
			}
			
			if (!this.log_file.exists())
			{
				try
				{
					this.log_file.createNewFile();
				}
				catch (final IOException e)
				{
					this.echo(e, Constants.log_type.error);
				}
			}
			else if (this.log_file.isDirectory())
			{
				this.echo("The specified log file (" + this.log_file.getAbsolutePath()
						+ ") is a folder!", Constants.log_type.error);
				return;
			}
			
			try
			{
				this.file_io = new BufferedWriter(new FileWriter(this.log_file, true));
			}
			catch (final IOException e)
			{
				this.echo(e, Constants.log_type.error);
			}
		}
		else
		{
			this.path = null;
			this.log_file = null;
		}
	}
	
	private void echo(final Object message)
	{
		this.echo(message, Constants.log_type.message);
	}
	
	private void echo(final Object message, final Constants.log_type type)
	{
		final Logger log = cellngine.getConsoleLog();
		if (log == this)
		{
			if (type == Constants.log_type.error)
			{
				System.err.println(message);
			}
			else
			{
				System.out.println(message);
			}
		}
		else if (log != null)
		{
			log.log(message, type);
		}
	}
	
	public void log(final Object o, final Constants.log_type type)
	{
		if (o instanceof Exception)
		{
			this.log((Exception) o, type);
		}
		else if (o instanceof String)
		{
			this.log((String) o, type);
		}
		else
		{
			this.log(o.toString(), type);
		}
	}
	
	private void log(final Exception e, final Constants.log_type type)
	{
		StringWriter stringWriter = new StringWriter();
		PrintWriter printWriter = new PrintWriter(stringWriter);
		e.printStackTrace(printWriter);
		this.log(stringWriter.toString(), type);
	}
	
	private synchronized void log(final String message, final Constants.log_type type)
	{
		if (this.path == null)
		{
			this.echo(message, type);
		}
		else
		{
			if (!this.log_file.exists())
			{
				this.echo("The log file was deleted!", Constants.log_type.error);
				return;
			}
			
			try
			{
				this.file_io.append(message);
				this.file_io.newLine();
				this.file_io.flush();
			}
			catch (final IOException e)
			{
				this.echo("Could not write to " + this.log_file.getAbsolutePath() + "!", type);
				return;
			}
		}
	}
}